package com.jeasonzhao.report.vml.chart;

import com.jeasonzhao.report.vml.chart.enums.RuleTypes;
import com.jeasonzhao.report.vml.exception.RuleCalcException;


public class RuleInfo
{
    private RuleTypes m_RuleType = RuleTypes.Default;
    private double m_lfMaxValue = 0;
    private double m_lfMinValue = 0;
    private int m_nInitRulesCount = 10;
    private int m_nPasitiveRulesCount = 0; //正向刻度数量
    private int m_nNegativeRulesCount = 0; //负向刻度数量
    private double m_BaseLineValue = 0; //基线的值
    private boolean m_isAlign2Zero = true; //基线0对齐
    private double m_lfStepValue = 1; //每个刻度之间的值

    private static double log10(double lfValue)
    {
        return Math.log(lfValue) / Math.log(10);
    }

    public RuleInfo(double lfMin,double lfMax,int nCount,boolean b)
    {
        m_lfMinValue = lfMin;
        m_lfMaxValue = lfMax;
        m_nInitRulesCount = nCount;
        m_isAlign2Zero = b;
    }

    public RuleInfo(RuleTypes r)
    {
        m_RuleType = r;
    }

    public int getRulesCount()
    {
        return this.m_nPasitiveRulesCount + this.m_nNegativeRulesCount;
    }

    public double getRuleMax()
    {
        return this.m_BaseLineValue + m_nPasitiveRulesCount * this.m_lfStepValue;
    }

    public double[] ruleValues()
    {
        int nx = getRulesCount();
        double[] ret = new double[nx];
        for(int n = 0;n < nx;n++)
        {
            ret[n] = this.getRuleMin() + n * this.getStepValue();
        }
        return ret;
    }

    public double getRuleMin()
    {
        return this.m_BaseLineValue + (0 - m_nNegativeRulesCount) * this.m_lfStepValue;
    }

    public RuleTypes getRule()
    {
        return m_RuleType;
    }

    public void setRule(RuleTypes rule)
    {
        this.m_RuleType = rule;
    }

    public void setMin(double min)
    {
        this.m_lfMinValue = min;
    }

    public void setMax(double max)
    {
        this.m_lfMaxValue = max;
    }

    public void reset()
    {
        m_lfMaxValue = 0 - Double.MAX_VALUE;
        m_lfMinValue = Double.MAX_VALUE;
        this.m_nNegativeRulesCount = 0;
        this.m_nPasitiveRulesCount = 0;
    }

    public double getBaseLineValue()
    {
        return m_BaseLineValue;
    }

    public boolean isAlign2Zero()
    {
        return m_isAlign2Zero;
    }

    public int getNegativeRulesCount()
    {
        return m_nNegativeRulesCount;
    }

    public int getPasitiveRulesCount()
    {
        return m_nPasitiveRulesCount;
    }

    public double getStepValue()
    {
        return m_lfStepValue;
    }

    public void calculate()
        throws RuleCalcException
    {
        double lfMin = m_lfMinValue;
        double lfMax = m_lfMaxValue;
        if(lfMin > lfMax)
        {
            double x = lfMin;
            lfMin = lfMax;
            lfMax = x;
        }
        if(lfMin == lfMax)
        {
            lfMax += this.m_nInitRulesCount;
        }
        int rulesCount = m_nInitRulesCount;
        if(rulesCount < 1)
        {
            rulesCount = 10;
        }
        double lfBaseLineValue = 0;
        if(lfMin > 0 && lfMax > 0 && m_isAlign2Zero == false)
        {
            int xLog = (int) log10(lfMax);
            lfBaseLineValue = Math.floor(lfMin / Math.pow(10,xLog - 1)) * Math.pow(10,xLog - 1);
        }
        else if(lfMin < 0 && lfMax < 0 && m_isAlign2Zero == false)
        {
            int xLog = (int) log10(Math.abs(lfMin));
            lfBaseLineValue = 0 - Math.floor(Math.abs(lfMax) / Math.pow(10,xLog - 1)) *
                Math.pow(10,xLog - 1);
        }
        else
        {
            lfBaseLineValue = 0;
        }
        double lfStep = 0;
        int nNagative = 0;
        int nPasitive = 0;
        if(lfMin < 0 && lfMax > 0)
        {
            double lfRang = lfMax - lfMin;
            int nLog = (int) log10(lfRang);
            lfStep = Math.round(lfRang / (double) rulesCount / Math.pow(10,nLog - 1));
            lfStep = lfStep * Math.pow(10,nLog - 1);
            if(lfStep == 0)
            {
                lfStep = Math.round(lfRang / (double) rulesCount / Math.pow(10,nLog - 2));
                lfStep = lfStep * Math.pow(10,nLog - 2);
            }
            nNagative = (int) Math.ceil(Math.abs(lfMin) / lfRang * rulesCount);
            nPasitive = rulesCount - nNagative;
            while((lfBaseLineValue + nPasitive * lfStep) < lfMax)
            {
                nPasitive++;
            }
            while((lfBaseLineValue + 0 - nNagative * lfStep) > lfMin)
            {
                nNagative++;
            }
        }
        else if(lfMin < 0 && lfMax <= 0)
        {
            double lfRang = lfBaseLineValue - lfMin;
            int nLog = (int) log10(lfRang);
            lfStep = Math.round(lfRang / (double) rulesCount / Math.pow(10,nLog - 1));
            lfStep = lfStep * Math.pow(10,nLog - 1);
            if(lfStep == 0)
            {
                lfStep = Math.round(lfRang / (double) rulesCount / Math.pow(10,nLog - 2));
                lfStep = lfStep * Math.pow(10,nLog - 2);
            }
            while((lfBaseLineValue + 0 - rulesCount * lfStep) > lfMin)
            {
                rulesCount++;
            }
            nNagative = rulesCount;
        }
        else if(lfMin >= 0 && lfMax > 0)
        {
            double lfRang = lfMax - lfBaseLineValue;
            int nLog = (int) log10(lfRang);
            lfStep = lfRang / (double) rulesCount / Math.pow(10,nLog - 1);
            lfStep = Math.round(lfStep);
            lfStep = lfStep * Math.pow(10,nLog - 1);
            if(lfStep == 0)
            {
                lfStep = lfRang / (double) rulesCount / Math.pow(10,nLog - 2);
                lfStep = Math.round(lfStep);
                lfStep = lfStep * Math.pow(10,nLog - 2);
            }
            while((lfBaseLineValue + rulesCount * lfStep) < lfMax)
            {
                rulesCount++;
            }
            nPasitive = rulesCount;
        }
        //Assign values
        m_nPasitiveRulesCount = nPasitive;
        m_nNegativeRulesCount = nNagative;
        m_BaseLineValue = lfBaseLineValue;
        m_lfStepValue = lfStep;
        System.out.println(
            this.getRule() + " " +
            "基线值" + lfBaseLineValue +
            "最大值 " + lfMax + "[正向数量" + nPasitive + "] [" + (lfBaseLineValue + lfStep * nPasitive) + "]" +
            "最小值" + lfMin + "[负向数量" + nNagative + "] [" + (lfBaseLineValue + 0 - lfStep * nNagative) + "]" +
            " 步进 " + m_lfStepValue);
    }

    public void calculate(RuleInfo rule)
        throws RuleCalcException
    {
        if(rule == null)
        {
            calculate();
            return;
        }
        if(rule.getNegativeRulesCount() + rule.getPasitiveRulesCount() <= 0)
        {
            throw new RuleCalcException("输入的参数不正确！");
        }
        double lfMin = m_lfMinValue;
        double lfMax = m_lfMaxValue;
        if(lfMin > lfMax)
        {
            double x = lfMin;
            lfMin = lfMax;
            lfMax = x;
        }
        if(lfMin == lfMax)
        {
            throw new RuleCalcException("最大值和最小值相等！");
        }
        int nPasitive = rule.getPasitiveRulesCount();
        int nNagative = rule.getNegativeRulesCount();
        if(nNagative <= 0 && lfMin < 0)
        {
            throw new RuleCalcException("负向坐标不匹配 "+nNagative+"/"+lfMin+">"+lfMax);
        }
        if(nPasitive <= 0 && lfMax > 0)
        {
            throw new RuleCalcException("正向坐标不匹配");
        }
        //同步baseline
        this.m_isAlign2Zero = rule.m_isAlign2Zero;
        double lfBaseLineValue = 0;
        if(lfMin > 0 && lfMax > 0 && m_isAlign2Zero == false && rule.getBaseLineValue() != 0)
        {
            int xLog = (int) log10(lfMax);
            lfBaseLineValue = Math.floor(lfMin / Math.pow(10,xLog - 1)) * Math.pow(10,xLog - 1);
        }
        else if(lfMin < 0 && lfMax < 0 && m_isAlign2Zero == false && rule.getBaseLineValue() != 0)
        {
            int xLog = (int) log10(Math.abs(lfMin));
            lfBaseLineValue = 0 - Math.floor(Math.abs(lfMax) / Math.pow(10,xLog - 1)) *
                Math.pow(10,xLog - 1);
        }
        else
        {
            lfBaseLineValue = 0;
        }
        double lfStep = 0;
        if(lfMin < 0 && lfMax > 0)
        {
            double lfRang = lfMax - lfMin;
            int nLog = Math.max((int) log10(lfMax),(int) log10(0 - lfMin));
            lfStep = Math.max(
                lfMax / (double) (nPasitive),
                (0 - lfMin) / (double) (nNagative));
            lfStep = Math.round(lfStep / Math.pow(10,nLog - 1));
            lfStep = lfStep * Math.pow(10,nLog - 1);
        }
        else if(lfMin < 0 && lfMax <= 0)
        {
            double lfRang = lfBaseLineValue - lfMin;
            int nLog = (int) log10(lfRang);
            lfStep = Math.round(lfRang / (double) (nNagative) / Math.pow(10,nLog - 1));
            lfStep = lfStep * Math.pow(10,nLog - 1);
            if((lfBaseLineValue + 0 - (nNagative) * lfStep) > lfMin)
            {
                lfStep = Math.ceil(lfRang / (double) (nNagative) / Math.pow(10,nLog - 1));
                lfStep = lfStep * Math.pow(10,nLog - 1);
            }
        }
        else if(lfMin >= 0 && lfMax > 0)
        {
            double lfRang = lfMax - lfBaseLineValue;
            int nLog = (int) log10(lfRang);
            lfStep = Math.round(lfRang / (double) (nPasitive) / Math.pow(10,nLog - 1));
            lfStep = lfStep * Math.pow(10,nLog - 1);
            while((lfBaseLineValue + (nPasitive) * lfStep) < lfMax)
            {
                lfStep = Math.ceil(lfRang / (double) (nPasitive) / Math.pow(10,nLog - 1));
                lfStep = lfStep * Math.pow(10,nLog - 1);
            }
        }
        //Assign values
        m_nPasitiveRulesCount = nPasitive;
        m_nNegativeRulesCount = nNagative;
        m_BaseLineValue = lfBaseLineValue;
        m_lfStepValue = lfStep;
//        System.out.println(this.getRule() + " " +
//                           "基线值" + lfBaseLineValue + "最大值 " + lfMax + "[正向数量" + nPasitive + "] [" + (lfBaseLineValue + lfStep * nPasitive) + "]" +
//                           "最小值" + lfMin + "[负向数量" + nNagative + "] [" + (lfBaseLineValue + 0 - lfStep * nNagative) + "]" +
//                           " 步进 " + m_lfStepValue);
    }

    public void setAlign2Zero(boolean align2Zero)
    {
        this.m_isAlign2Zero = align2Zero;
    }

    public void setInitRulesCount(int initRulesCount)
    {
        this.m_nInitRulesCount = initRulesCount;
    }

    public int getWeight()
    {
        return(this.m_nNegativeRulesCount > 0 ? -1 : 0) +
            (this.m_nPasitiveRulesCount > 0 ? 1 : 0);
    }

    public double getWeightRate()
    {
        if(this.getWeight() == 0)
        {
            return 1;
        }
        return this.m_nPasitiveRulesCount / m_nNegativeRulesCount;
    }

    public static void main(String[] argv)
        throws Exception
    {
        RuleInfo g = null;
        RuleInfo g2 = null;
        g = new RuleInfo( -2120.0, -1,5,false);
        g2 = new RuleInfo( -222.0, -1023,5,false);
        g.calculate();
        g2.calculate(g);

        g = new RuleInfo( -2120.0, -1999,5,false);
        g2 = new RuleInfo( -222.0, -13232324,5,false);
        g.calculate();
        g2.calculate(g);

        g = new RuleInfo(2120.0,1,5,false);
        g2 = new RuleInfo(222.0,1023,5,false);
        g.calculate();
        g2.calculate(g);

        g = new RuleInfo(2120.0,1999,5,false);
        g2 = new RuleInfo(222.0,13232324,5,false);
        g.calculate();
        g2.calculate(g);

        g = new RuleInfo( -222.0,13232324,3,false);
        g2 = new RuleInfo( -0.000002,0.000009,3,false);
        g.calculate();
        g2.calculate(g);

    }
}
